/**
 * bind()方法创建一个新的函数, 当被调用时，将其this关键字设置为提供的值，在调用新函数时，在任何提供之前提供一个给定的参数序列
 * apply 和 call 的区别是 call 方法接受的是若干个参数列表，而 apply 接收的是一个包含多个参数的数组
 */

Function.prototype.Call = function (context) {
    //如果没有传或传的值为空对象 context指向window
    context = context || window
    //给context添加一个方法 指向this
    context.fn = this
    //处理参数去除第一个参数this 其它传入fn函数
    let args = [...arguments].slice(1)
    //将参数赋值给函数
    let result = context.fn(...args)
    //删除方法
    delete context.fn
    return result
}

Function.prototype.Apply = function (context) {
    context = context || window
    context.fn = this
    //取出数据参数
    let args = arguments[1]
    let result
    if (args) {
        result = context.fn(...args)
    } else {
        result = context.fn()
    }
    delete context.fn
    return result
}

Function.prototype.Bind = function (context) {
    if (typeof this !== 'function') {
        // closest thing possible to the ECMAScript 5
        // internal IsCallable function
        throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
    }

    let _this = this
    // 可以支持柯里化传参，保存参数;  函数有可能是fn.bind(obj, 1)(2)
    let args = [...arguments].slice(1)
    return function F() {
        //同样因为支持柯里化形式传参我们需要再次获取存储参数
        let newArgs = args.concat([...arguments]);
        // 判断是否被当做构造函数使用
        if (this instanceof F) {
            return _this.apply(this, newArgs)
        }
        return _this.apply(context, newArgs)
    }
}
